package linker

import (
	"bytes"
	"debug/elf"
	"fmt"
	"os"
	"rvld/src/utils"
)

// check if is unix elf file format
func CheckMagic(contents []byte) bool {
	return bytes.HasPrefix(contents, []byte("\177ELF")) // NOTE: this is magic field
}

// write unix elf field to a series of byte
func WriteMagic(contents []byte) {
	copy(contents, "\177ELF") // NOTE: write magic field to `contents`
}

func GetFileType(contents []byte) FileType {
	if len(contents) == 0 {
		return FileTypeEmpty
	}

	if CheckMagic(contents) {
		// NOTE: read `contents` at position 16 then to the end of `contents`
		et := elf.Type(utils.ThisRead[uint16](contents[16:]))
		switch et {
		case elf.ET_REL:
			return FileTypeObject
		default:
			fmt.Println("[DEBUG]: the file type is unsupported to parse now, I don't know what the filetype")
		}
		return FileTypeUnknown
	}

	if bytes.HasPrefix(contents, []byte("!<arch>\n")) {
		return FileTypeArchive
	}

	return FileTypeUnknown
}

// `filename` must exists
func MustNewFile(filename string) *File {
	contents, err := os.ReadFile(filename)
	utils.ThisMustNo(err)
	return &File{
		Name:     filename,
		Contents: contents,
	}
}

// `filepath` must exists
func MustOpenLibrary(filepath string) *File {
	contents, err := os.ReadFile(filepath)
	utils.ThisMustNo(err)

	return &File{
		Name:     filepath,
		Contents: contents,
	}
}

func FindLibrary(ctx *Context, name string) *File {
	for _, dir := range ctx.Args.LibraryPaths {
		// library name format ?
		stem := dir + "/lib" + name + ".a"
		if f := MustOpenLibrary(stem); f != nil {
			return f
		}
	}
	utils.ThisFatal("[ERROR]: Library not found")
	return nil
}

func CheckFileCompatibility(ctx *Context, file *File) {
	mt := GetMachineTypeFromContents(file.Contents)
	if mt != ctx.Args.Emulation {
		utils.ThisFatal("[ERROR]: Incompatible file type")
	}
}

// DONE
